from api import api


def get_store_models():
    _models = {}
    for addon in api.addons:
        name = ''.join([wd.capitalize() for wd in addon._name.split('.')])
        name = name[0].lower() + name[1:]
        _models[name] = addon
    return _models


class StoreModelBase(object):
    _instances = {}
    _models = get_store_models()

    def __new__(cls, name, Model=None):
        ins = cls._instances.get(name)
        if ins:
            return ins

        if not Model:
            Model = cls._models[name]

        ins = object.__new__(cls)
        cls._instances[name] = ins
        ins._name = name
        ins.model = Model._name
        ins.Model = api.env[Model._name]
        ins.metadate = ins.Model.metadata
        ins.api = api
        ins.env = api.env

        ins.fieldsGet = {}  # feilds_get 返回结果
        ins.dataList = []  # search 方法 返回结果 list page

        # browse 方法 返回的结果 view page,
        # default_get, onchange, 返回的结果, 原始数据 在 dateDict 暂存
        ins.dataDict = {}

        ins.formData = {}  # default_get, onchange, 返回的结果, 给 form page 用
        ins.values = {}  # 给 form page 提交用
        ins.valuesForParent = {
            # 本模型指向父模型的 m2o字段, 及父模型 的值
            # 'm2o_id': {'company_id': 0}
        }

        # ins.dataList = []  # 子表 显示 list 时需要
        ins.listO2m = []  # 所有的子表数据
        ins.dataId = 0  # 作为明细行, 新增/编辑时, 暂存id

        # listO2m 的格式
        # listO2m = [
        #   (4, id1, dataDict, fromData={}, values={})
        #   (2, id2, None, None, None),      # 如果删除了, 直接这里删除.
        #   (1, id3, dataDict, formData, values ),  # 已经被编辑了, 重复更新
        #   (4, id1, dataDict, fromData={}, values={})
        #   (0, -id1, {}, formData, values), # 新增, 生成临时 id
        #   (0, -id2, {}, formData, values), # 新增, 生成临时 id
        # ]

        return ins

    def __init__(self, *args, **kwargs):
        pass

    def __getattr__(self, index):
        obj = api.env[self.model]
        return obj[index]

    def __getitem__(self, index):
        if hasattr(self, index):
            return getattr(self, index)

        obj = api.env[self.model]
        return obj[index]


class StoreModelCrud(StoreModelBase):
    def search(self, domain, isEdit=None):
        result = self.Model.web_search_read(domain)
        self.dataList = result['records']
        self.dataLength = result['length']
        if isEdit:
            # op, rid, dataDict, formData, values
            # formData 为空, 暂时没有被编辑
            self.listO2m = [(4, rec['id'], rec, {}, {})
                            for rec in result['records']]

    def browse_one(self, rid, isEdit=None, ):
        result = self.Model.browse_one(rid, isEdit=isEdit)

        # 异步处理 o2m 字段
        cols = self.Model.metadata['columnsForPage']

        for f, v in result.items():
            meta = cols.get(f, {})
            if meta and meta.get('type') == 'table':
                # 刷新 子 的 dataList
                o2m_store = self.__class__(
                    meta['vuxModel'], self._models[meta['vuxModel']])
                o2m_store.search([('id', 'in', v)], isEdit=isEdit)

        self.dataDict = result
        if not isEdit:
            return

        self.dataDict = dict(result)
        self.formData = self.to_formData(result)
        self.values = {}
        self.dataId = result['id']

    def submitCreate(self):
        # print('----in store---submitCreate', self.values)
        res = self.Model.create(self.values)
        # print('----in store---submitCreate 2, --- ', res)
        self.dataDict = res

    def submitWrite(self, rid):
        # print('----in store---submitCreate', self.values)
        res = self.Model.write(rid, self.values)
        print('----in store---submitWrite 2, --- ', res)

        self.dataDict = res

    def unlink(self, rid):
        print('----in store---unlink', rid)
        res = self.Model.unlink(rid, )
        print('----in store---unlink 2, --- ', res)


class StoreModelNew(StoreModelCrud):
    def _name_search_field(self, field, domain=None):
        meta = self.fieldsGet[field]
        if meta['type'] not in ['many2one']:
            return
        if not domain:
            domain = meta['domain']

        return self.Model.env[meta['relation']].name_search(args=domain, limit=0)

    def to_formData(self, values):
        def fn(k, v):
            meta = self.fieldsGet.get(k)
            if meta and meta.get('type') in['selection', 'many2one']:
                return v and v[0]
            return v

        return dict((k, fn(k, v)) for k, v in values.items() if k != 'id')

    def to_dataDict(self, values):
        def fn(field, v):
            meta = self.fieldsGet.get(field)
            if meta and meta.get('type') in['selection', 'many2one']:
                ops = dict(meta['options'])
                return [v, ops[v]]
            return v

        return dict((field, fn(field, v)) for field, v in values.items())

    def _after_fields_get(self, result):
        # 设置 options
        for k in result:
            meta = self.fieldsGet[k]
            if meta['type'] == 'many2one' and not meta.get('_set') and isinstance(meta['domain'], list):
                ops = self._name_search_field(k)
                # 异步操作, 判断, 是否已经设置过
                meta2 = self.fieldsGet[k]
                if not meta.get('_set'):
                    meta2['options'] = ops
                    meta2['_set'] = 'init'

    def fields_get(self, globals_dict=None, fields_type=None):
        values = dict(self.valuesForParent, **self.formData)
        # print(' -- in store --,fields_get ', self.valuesForParent, values)
        globals_dict = self.Model.get_globals_dict(
            values, globals_dict=globals_dict)
        result = self.Model.fields_get2(
            globals_dict=globals_dict, fields_type=fields_type)
        self.fieldsGet.update(result)
        self._after_fields_get(result)

    def _after_onchange_value(self, dataDict):
        self.dataDict.update(dataDict)
        values = self.to_formData(dataDict)
        self.formData.update(values)
        self.values.update(values)

    def _after_onchange_set_options_first(self, dataDict):
        # 初步设置 options
        for k, v in dataDict.items():
            meta = self.fieldsGet.get(k, {})
            if meta.get('type') == 'many2one':
                # 先从 数据中 取 初始值
                if not meta.get('_set'):
                    if dataDict.get(k):
                        meta['options'] = v
                    else:
                        meta['options'] = []

    def _after_onchange_domain(self, domain):
        # 获取 onchange 后, 会返回 m2o 字段 的 domain
        # 初步设置 options
        for k, v in domain.items():
            meta = self.fieldsGet.get(k, {})
            if meta.get('type') == 'many2one':
                # 如果 这里的 domain 变了, 那么重新获取 ops
                ops = self._name_search_field(k, meta['domain'] + v)
                # 异步操作, 强制设置
                meta['_set'] = 'init'
                meta['options'] = ops

    def _after_onchange(self, result):
        # print('----_after_onchange------', result)

        # 1 更新 fieldsGet
        fieldsGet = result['fields']
        self.fieldsGet.update(dict((k, dict(self.fieldsGet.get(k, {}), **v))
                                   for k, v in fieldsGet.items()))

        # 2 更新 fieldsGet 的 options, 仅仅 用获取的值来更新
        self._after_onchange_set_options_first(result['value'])

        # 3 更新 dataDict, formData, values
        self._after_onchange_value(result['value'])

        # 4 更新 fieldsGet 的 options, 根据 domain 来更新
        self._after_onchange_domain(result['domain'])

        # 5 因为第1步可能带回来新的 m2o, 或者domian 变化过的, 因此 再刷新一遍 options
        self._after_fields_get(fieldsGet)

    def default_get_onchange(self, context=None):
        # print('--in--store--,default_get_onchange ', context)

        self.dataDict = {}
        self.formData = {}
        self.values = {}
        self.dataId = 0

        values = dict(self.valuesForParent)
        result = self.Model.default_get_onchange(
            values=values, context=context)

        self._after_onchange(result)

        # 清空 o2m 的 dataList, 为下一步做准备
        cols = self.Model.metadata.get('columnsForPage', {})
        for f in result['value']:
            meta = cols.get(f, {})
            if meta and meta.get('type') == 'table':
                # # o2m 的值 用于编辑, 需要 置空
                # result['value'][f] = []
                # 置空 o2m 的 dataList
                o2m_store = self.__class__(meta['vuxModel'])
                o2m_store.dataList = []

    def onchange(self, field, value, context=None):
        # page 编辑 更新 formData
        # print('--xxxxx------ onchange, ', self._name, field, value)
        values = dict([(field, value)])
        self.values.update(values)
        self.formData.update(values)
        self.dataDict.update(self.to_dataDict(values))
        # print('--xxxxx------ onchange,self.values ', self._name,  self.values)
        # print('--xxxxx------ onchange,self.formData ',
        #       self._name,  self.formData)
        # print('--xxxxx------ onchange,self.dataDict ', self._name, self.dataDict)

        values2 = dict(self.valuesForParent, **self.formData)

        ids = self.dataDict.get('id', 0)
        ids = ids > 0 and [ids] or []

        result = self.Model.onchange2(
            ids, values2, field, context=context)
        # print('--123567890--onchange--,', self._name, result)

        self._after_onchange(result)


class StoreModelForO2m(StoreModelNew):
    def set_valuesForParent(self, valuesForParent):
        self.valuesForParent = valuesForParent

    def _get_o2m_store(self, field):
        o2m_meta = self.Model.metadata['columnsForPage'][field]
        o2m_model = o2m_meta['vuxModel']
        o2m_store = self.__class__(o2m_model)
        return o2m_store

    # def _get_o2m_parent_ref(self, field):
    #     o2m_meta = self.Model.metadata['columnsForPage'][field]
    #     return o2m_meta['refName']

    def set_o2mForm(self, field):
        # 前提,
        # 1 父 如果是新增, o2m 的 list = []
        # 2 父 如果是编辑, o2m 的 list = [...] 被异步更新
        # 这是第一步,
        # 1 取 parent values
        # 2 取 fields_get
        # parent_field = self._get_o2m_parent_ref(field)
        o2m_store = self._get_o2m_store(field)
        parent_field = self.Model.metadata['columnsForPage'][field]['refName']

        # o2m_values = {}
        # o2m_values[parent_field] = self.formData.copy()
        # o2m_store.set_valuesForParent(o2m_values)

        o2m_store.valuesForParent = dict([(parent_field, dict(self.formData))])
        o2m_store.fields_get()

    def o2m_default_get_onchange(self, field):
        # 这是这是 第二步, 新增分支, 新增明细
        # 1 取 o2m 默认值
        # 2 更新 自己的 dataDict, formData, values
        o2m_store = self._get_o2m_store(field)
        o2m_store.default_get_onchange()

        # 新增 明细行, call default_get_onchange
        # 更新 明细行的 dataDict, formData, values
        # 打开 明细行的 formpage ok

    def o2m_search_one(self, rid_src, with_index=None):
        # res = [(op, rid, dataDict, formData, values)
        #        for op, rid, dataDict, formData, values in self.listO2m if rid == rid_src]
        # if not res:
        #     raise ValueError("no line")
        # return res[0]

        for index, ln in enumerate(self.listO2m):
            if ln[1] == rid_src:
                if with_index:
                    return ln, index
                else:
                    return ln

        raise ValueError("no line")

    def o2m_edit(self, field, o2m_id):
        # 这是第二步, 明细编辑 分支
        # 新增后, 编辑新增行
        # print('----1--o2m_edit--1--,',  self._name, field, o2m_id)

        o2m_store = self._get_o2m_store(field)
        o2m_line, o2m_index = o2m_store.o2m_search_one(o2m_id, with_index=1)

        op, o2m_rid, o2m_dataDict, o2m_formData, o2m_values = o2m_line
        if op == 2:  # del, edit
            raise ValueError("del to edit")
        elif op == 4:  # old, edit
            o2m_store.listO2m[o2m_index] = (
                1, o2m_rid, o2m_dataDict, o2m_formData, o2m_values)
            o2m_formData = o2m_store.to_formData(o2m_dataDict)
        elif op == 0:  # new, edit
            pass
        elif op == 1:  # old, edit, edit
            pass

        # print('----1--o2m_edit--2--,',  self._name, o2m_line, o2m_index)
        # print('----1--o2m_edit--3--,',  self._name,
        #       o2m_dataDict, o2m_formData, o2m_values)

        o2m_store.dataId = o2m_rid
        o2m_store.dataDict = dict(o2m_dataDict)
        o2m_store.formData = dict(o2m_formData)
        o2m_store.values = dict(o2m_values)

        # print('----1--o2m_edit--1--,',  self._name, field, o2m_id)
        # print('----2--o2m_edit--1--,',   self._name,   o2m_store.dataId)
        # print('----3--o2m_edit--1--,',  self._name,   o2m_store.dataDict)
        # print('----4--o2m_edit--1--,',   self._name,   o2m_store.formData)

    def o2m_onchange(self, field, o2m_field, o2m_value):

        # 这是第三步, 明细行 录入数据
        # 2 更新 自己的 dataDict, formData, values
        # 3 更新 自己的 dataList, listO2m
        # 4 触发父表 的 onchange
        o2m_store = self._get_o2m_store(field)
        # print('----1--o2m_onchange--1--,',
        #       self._name, field, o2m_field, o2m_value, o2m_store.dataId)
        # print('----2--o2m_onchange--1--,',   self._name,   o2m_store.dataId,)
        # print('----3--o2m_onchange--1--,',  self._name,   o2m_store.dataDict,)
        # print('----4--o2m_onchange--1--,',   self._name,   o2m_store.formData,)

        o2m_store.onchange(o2m_field, o2m_value)

        o2m_id = o2m_store.dataId

        if not o2m_id:  # new edit
            # 做临时id
            ids = [line[1] for line in o2m_store.listO2m]
            min_id = min(ids or [0])
            o2m_id = (min_id < 0 and min_id or 0) - 1
            o2m_store.dataId = o2m_id

            o2m_line = (0, o2m_id, dict({'id': o2m_id}, **o2m_store.dataDict),
                        dict(o2m_store.formData), dict(o2m_store.values))
            o2m_store.listO2m.append(o2m_line)

        else:  # new eidt, old edit
            o2m_line, line_index = o2m_store.o2m_search_one(
                o2m_id, with_index=1)

            o2m_store.listO2m[line_index] = (
                o2m_line[0], o2m_line[1],
                dict({'id': o2m_line[1]}, **o2m_store.dataDict),
                dict(o2m_store.formData), dict(o2m_store.values))

        o2m_store.dataList = [ln[2] for ln in o2m_store.listO2m if ln[0] != 2]

    def o2m_submit(self, field):
        o2m_store = self._get_o2m_store(field)

        lines = [(ln[0], ln[1], ln[0] in (1, 0) and ln[3] or None)
                 for ln in o2m_store.listO2m]

        self.onchange(field, lines)

    def o2m_del(self, field, o2m_id):
        o2m_store = self._get_o2m_store(field)
        # o2m_id = o2m_store.dataId
        if not o2m_id:  # new edit
            raise ValueError("no line")

        o2m_line, o2m_index = o2m_store.o2m_search_one(o2m_id, with_index=1)
        op = o2m_line[0]

        if op == 2:  # del, del
            raise ValueError("del to del")
        elif op in [4, 1]:  # old, del; # old, edit, del
            o2m_store.listO2m[o2m_index] = (2, o2m_line[1], None, None, None)
        elif op == 0:  # new, del
            o2m_store.listO2m = [ln for ln in o2m_store.listO2m
                                 if ln[1] != o2m_id]
        o2m_store.dataList = [ln[2] for ln in o2m_store.listO2m if ln[0] != 2]
        self.o2m_submit(field)


class StoreModel(StoreModelForO2m):
    pass
